package com.example.videocompressors;

import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Matrix;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PropertyKey;
import ohos.media.image.common.Size;
import ohos.utils.net.Uri;

import java.io.*;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class SiliCompressor {
    private static final String IMAGE_DESTINATION = "Silicompressor/images";
    static volatile SiliCompressor singleton = null;
    private static Context mContext;
    private boolean isResourceTable = true;

    public SiliCompressor(Context context) {
        mContext = context;
    }

    public static SiliCompressor with(Context context) {
        if (singleton == null) {
            synchronized (SiliCompressor.class) {
                if (singleton == null) {
                    singleton = new SiliCompressor(context);
                }
            }
        }
        return singleton;
    }

    public static byte[] readStream(InputStream inStream) throws Exception {
        byte[] buffer = new byte[16];
        int len = -1;
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        byte[] data = outStream.toByteArray();
        outStream.close();
        inStream.close();
        return data;
    }

    private static boolean deleteImageFile(String imageUri) {
        Uri uri = Uri.parse(imageUri);
        String pathName = uri.getDecodedPath();
        if (pathName.startsWith("/")) {
            int len = pathName.length();
            pathName = pathName.substring(1, len);
        }
        File sourceFile = new File(pathName);
        if (!sourceFile.exists()) {
            try {
                sourceFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (sourceFile.isFile()) {
                return deleteFile(pathName);
            } else {
                return deleteDirectory(pathName);
            }
        } else {
            if (sourceFile.isFile()) {
                return deleteFile(pathName);
            } else {
                return deleteDirectory(pathName);
            }
        }
    }

    public static boolean deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.isFile() && file.exists()) {
            return file.delete();
        }
        return false;
    }

    public static boolean deleteDirectory(String filePath) {
        boolean isFlag = false;
        if (!filePath.endsWith(File.separator)) {
            filePath = filePath + File.separator;
        }
        File dirFile = new File(filePath);
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        isFlag = true;
        File[] files = dirFile.listFiles();
        for (int i1 = 0; i1 < files.length; i1++) {
            if (files[i1].isFile()) {
                try {
                    isFlag = deleteFile(files[i1].getCanonicalPath());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (!isFlag) break;
            } else {
                try {
                    isFlag = deleteDirectory(files[i1].getCanonicalPath());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (!isFlag) break;
            }
        }
        if (!isFlag) return false;
        return dirFile.delete();
    }

    public PixelMap getCompressBitmap(String imageUri, boolean isDeleteSourceImage, String tmpDir) throws IOException {
        String compressedImageUriString = compressImage(imageUri, new File(tmpDir));
        PixelMap bitmap = null;
        File desFile = new File(compressedImageUriString);
        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
        ImageSource imageSource = ImageSource.create(desFile, srcOpts);
        ImageSource.DecodingOptions decodingOpts = null;
        if (imageSource != null) {
            decodingOpts = new ImageSource.DecodingOptions();
        }
        bitmap = imageSource.createPixelmap(decodingOpts);
        if (isDeleteSourceImage) {
            boolean isDeleted = deleteImageFile(imageUri);
        }
        return bitmap;
    }

    public String compress(String uriString, File destination) {
        return compressImage(uriString, destination);
    }

    private int calculateInSampleSize(ImageSource.DecodingOptions options, int reqWidth, int reqHeight) {
        final int height = options.desiredSize.height;
        final int width = options.desiredSize.width;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = Math.min(heightRatio, widthRatio);
        }
        final float totalPixels = width * height;
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;
        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
        return inSampleSize;
    }

    public PixelMap getCompressBitmap(String imagePath, String tmpDir) throws IOException {
        return getCompressBitmap(imagePath, false, tmpDir);
    }

    private PixelMap getPixelMapFromResource(int resourceId) {
        try (InputStream inputStream = mContext.getResourceManager().getResource(resourceId)) {
            ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
            srcOpts.formatHint = "image/jpg";
            ImageSource imageSource = ImageSource.create(inputStream, srcOpts);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            return imageSource.createPixelmap(decodingOptions);
        } catch (IOException | NotExistException e) {
            // do Nothing here
        }
        return null;
    }

    public String compress(int drawableID, String outputPath) {
        isResourceTable = false;
        PixelMap bitmap = getPixelMapFromResource(drawableID);
        if (bitmap != null) {
            File destDirectory = new File(outputPath);
            String timeStmp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
            String imageFileName = "JPEG_" + timeStmp + "_";
            String compressedImagePath = null;
            try {
                File imagefile = File.createTempFile(imageFileName, ".jpg", destDirectory);
                FileOutputStream outputStream = new FileOutputStream(imagefile);
                ImagePacker imagePacker = ImagePacker.create();
                ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
                packingOptions.format = "image/jpeg";
                packingOptions.quality = 80;
                imagePacker.initializePacking(outputStream, packingOptions);
                boolean isOsResult = imagePacker.addImage(bitmap);
                long dataSize = imagePacker.finalizePacking();
                imagePacker.release();
                Uri copyImageUri = Uri.getUriFromFile(imagefile);
                compressedImagePath = compressImage(imagefile.getCanonicalPath(), new File(outputPath));
                if (imagefile.exists()) {
                    deleteImageFile(copyImageUri.toString());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return compressedImagePath;
        }
        return null;
    }

    public String compress(String uriString, File destination, boolean isDeleteSourceImage) {
        String compressedImagePath = compressImage(uriString, destination);
        if (isDeleteSourceImage) {
            boolean isDeleted = deleteImageFile(uriString);
        }
        return compressedImagePath;
    }

    private String compressImage(String uriString, File destDirectory) {
        Uri imageUri = Uri.parse(uriString);
        PixelMap scaledBitmap = null;
        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
        srcOpts.formatHint = "image/jpeg";
        String pathName = imageUri.getDecodedPath();
        InputStream snputStream = null;
        if (!isResourceTable) {
            try {
                snputStream = new FileInputStream(uriString);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        } else {
            try {
                snputStream = new FileInputStream(pathName);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        byte[] data = null;
        try {
            data = readStream(snputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ImageSource imageSource = ImageSource.create(data, srcOpts);
        ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
        scaledBitmap = imageSource.createPixelmap(decodingOpts);
        int actualHeight = scaledBitmap.getImageInfo().size.height;
        int actualWidth = scaledBitmap.getImageInfo().size.width;
        decodingOpts.desiredSize = new Size(actualWidth, actualHeight);
        float maxHeight = 816.0f;
        float maxWidth = 612.0f;
        float imgRatio = actualWidth / actualHeight;
        float maxRatio = maxWidth / maxHeight;
        if (actualHeight > maxHeight || actualWidth > maxWidth) {
            if (imgRatio < maxRatio) {
                imgRatio = maxHeight / actualHeight;
                actualWidth = (int) (imgRatio * actualWidth);
                actualHeight = (int) maxHeight;
            } else if (imgRatio > maxRatio) {
                imgRatio = maxWidth / actualWidth;
                actualHeight = (int) (imgRatio * actualHeight);
                actualWidth = (int) maxWidth;
            } else {
                actualHeight = (int) maxHeight;
                actualWidth = (int) maxWidth;
            }
        }
        decodingOpts.desiredSize = new Size(actualWidth, actualHeight);
        decodingOpts.sampleSize = calculateInSampleSize(decodingOpts, actualWidth, actualHeight);
        int orientation = imageSource.getImagePropertyInt(PropertyKey.Exif.ORIENTATION, 0);
        if (orientation == 6) {
            decodingOpts.rotateDegrees = 90;
            decodingOpts.desiredSize = new Size(actualHeight, actualWidth);
        } else if (orientation == 3) {
            decodingOpts.rotateDegrees = 180;
        } else if (orientation == 8) {
            decodingOpts.rotateDegrees = 270;
            decodingOpts.desiredSize = new Size(actualHeight, actualWidth);
        }
        scaledBitmap = imageSource.createPixelmap(decodingOpts);
        float ratioX = actualWidth / (float) decodingOpts.desiredSize.width;
        float ratioY = actualHeight / (float) decodingOpts.desiredSize.height;
        float middleX = actualWidth / 2.0f;
        float middleY = actualHeight / 2.0f;
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
        Canvas canvas = new Canvas();
        canvas.setMatrix(scaleMatrix);
        PixelMapHolder pixelMapHolder = new PixelMapHolder(scaledBitmap);
        canvas.drawPixelMapHolder(pixelMapHolder, middleX - ((float) scaledBitmap.getImageInfo().size.width) / 2,
                middleY - ((float) scaledBitmap.getImageInfo().size.width) / 2,
                new Paint());
        String filename = getFilename(uriString, destDirectory);
        File desFile = new File(filename);
        try {
            FileOutputStream outputStream = new FileOutputStream(desFile);
            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
            packingOptions.format = "image/jpeg";
            packingOptions.quality = 80;
            imagePacker.initializePacking(outputStream, packingOptions);
            boolean isResult = imagePacker.addImage(scaledBitmap);
            long dataSize = imagePacker.finalizePacking();
            imagePacker.release();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        isResourceTable = true;
        return filename;
    }

    private String getFilename(String filename, File file) {
        if (isResourceTable) {
            try {
                if (!file.exists()) {
                    file.createNewFile();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            if (!file.exists()) {
                file.mkdirs();
            }
        }
        String ext = ".jpg";
        String nameFirstPart = null;
        try {
            nameFirstPart = file.getCanonicalPath()
                    + File.separator
                    + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        } catch (IOException e) {
            e.printStackTrace();
        }
        String nameFull = nameFirstPart + ext;
        int xx = 1;
        while (new File(nameFull).exists()) {
            nameFull = nameFirstPart + "_" + xx + ext;
            xx++;
        }
        return nameFull;
    }

    private String getFilename(File file) throws IOException {
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        String ext = ".jpg";
        String nameFirstPart = file.getCanonicalPath()
                + File.separator
                + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String nameFull = nameFirstPart + ext;
        int xxx = 1;
        while (new File(nameFull).exists()) {
            nameFull = nameFirstPart + "_" + xxx + ext;
            xxx++;
        }
        return nameFull;
    }

    public String compressVideo(String videoFilePath, String destinationDir) throws URISyntaxException {
        return compressVideo(videoFilePath, destinationDir, 0, 0, 0);
    }

    public String compressVideo(Uri videoContentUri, String destinationDir) throws URISyntaxException {
        return compressVideo(videoContentUri, destinationDir, 0, 0, 0);
    }

    public String compressVideo(String videoFilePath, String destinationDir, int outWidth, int outHeight, int bitrate)
            throws URISyntaxException {
        boolean isConverted = MediaController.getInstance(mContext)
                .convertVideo(videoFilePath,
                        new File(destinationDir),
                        outWidth,
                        outHeight,
                        bitrate);
        return MediaController.cachedFile.getPath();
    }

    public String compressVideo(Uri videoContentUri, String destinationDir, int outWidth, int outHeight, int bitrate)
            throws URISyntaxException {
        boolean isConverted = MediaController.getInstance(mContext)
                .convertVideo(mContext,
                        videoContentUri,
                        new File(destinationDir),
                        outWidth,
                        outHeight,
                        bitrate);
        return null;
    }
}
